package com.hzz.framework.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.hzz.framework.config.AppConfig;
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;

@AutoConfigureBefore(AppConfig.class)
@Component
@Slf4j
public class JwtUtil {
    //    @Autowired
//    private EhcacheUtil ehcacheUtil;
    @Value("${common.app-prefix}")
    private static String profiles;

    /**
     * 由字符串生成加密key
     */
    private static SecretKey generalKey() {
        String stringKey = profiles + CommonConstant.JWT_SECRET_KEY;
        byte[] encodedKey = Base64.decodeBase64(stringKey);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }

    /**
     * 创建jwt
     */
    public static String createJWT(UserType userType, String id, String subject, long ttlMillis) {
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        SecretKey key = generalKey();
        JwtBuilder builder = Jwts.builder()
                .setId(id)
                .setIssuedAt(now)
                .setSubject(subject)
                .claim("userType", userType)
                .signWith(SignatureAlgorithm.HS256, key);
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp);
        }
        return builder.compact();
    }

    /**
     * 解密jwt
     */
    public static Claims parseJWT(String jwt) {
        SecretKey key = generalKey();
        Claims claims = Jwts.parser()
                .setSigningKey(key)
                .parseClaimsJws(jwt).getBody();
        return claims;
    }

    /**
     * 生成subject信息
     */
    public static String generalSubject(Object obj, String[] fields) {
        JSONObject jo = new JSONObject();
        for (String field : fields) {
            jo.put(field, ReflectUtil.getValueByFieldName(obj, field));
        }
        return jo.toJSONString();
    }

    public static JSONObject getSubject() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String authorizationToken = request.getHeader(CommonConstant.JWT_HEADER_TOKEN_KEY);
        if (ValidatorUtil.isEmpty(authorizationToken)) {
            authorizationToken = request.getParameter(CommonConstant.JWT_HEADER_TOKEN_KEY);
        }
        if (ValidatorUtil.isEmpty(authorizationToken)) {
            throw new SignatureException("token头缺失");
        }
        final Claims claims = parseJWT(authorizationToken);
        return JSONObject.parseObject(claims.getSubject());
    }

    public static <T> T getSubject(Class<T> obj) {
        return getSubject().toJavaObject(obj);
    }

    public static <T> T getSubject(Object obj, Class<T> tClass) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String authorizationToken = request.getHeader(CommonConstant.JWT_HEADER_TOKEN_KEY);
        if (ValidatorUtil.isEmpty(authorizationToken)) {
            authorizationToken = request.getParameter(CommonConstant.JWT_HEADER_TOKEN_KEY);
        }
        if (ValidatorUtil.isEmpty(authorizationToken)) {
            authorizationToken = "" + ReflectUtil.getValueByFieldName(obj, CommonConstant.JWT_HEADER_TOKEN_KEY);
        }
        if (ValidatorUtil.isEmpty(authorizationToken)) {
            throw new SignatureException("token头缺失");
        }
        final Claims claims = parseJWT(authorizationToken);
        return JSONObject.parseObject(claims.getSubject()).toJavaObject(tClass);
    }

    public static boolean hasPermission(String authStr) {
        return isPermitted(authStr);
    }

    public static boolean isPermitted(String authStr) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String authorizationToken = request.getHeader(CommonConstant.JWT_HEADER_TOKEN_KEY);
        if (ValidatorUtil.isEmpty(authorizationToken)) {
            authorizationToken = request.getParameter(CommonConstant.JWT_HEADER_TOKEN_KEY);
        }
        if (ValidatorUtil.isEmpty(authorizationToken)) {
            throw new SignatureException("token头缺失");
        }
        final Claims claims = parseJWT(authorizationToken);
        JSONObject jobj = JSON.parseObject(claims.getSubject());
        JSONArray authorizationInfoPerms = jobj.getJSONArray("authorizationInfoPerms");
        return authorizationInfoPerms != null && authorizationInfoPerms.contains(authStr);
    }

    public static boolean isPermitted(Object obj, String authStr) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String authorizationToken = request.getHeader(CommonConstant.JWT_HEADER_TOKEN_KEY);
        if (ValidatorUtil.isEmpty(authorizationToken)) {
            authorizationToken = request.getParameter(CommonConstant.JWT_HEADER_TOKEN_KEY);
        }
        if (ValidatorUtil.isEmpty(authorizationToken)) {
            authorizationToken = "" + ReflectUtil.getValueByFieldName(obj, CommonConstant.JWT_HEADER_TOKEN_KEY);
        }
        if (ValidatorUtil.isEmpty(authorizationToken)) {
            throw new SignatureException("token头缺失");
        }
        final Claims claims = parseJWT(authorizationToken);
        JSONObject jobj = JSON.parseObject(claims.getSubject());
        JSONArray authorizationInfoPerms = jobj.getJSONArray("authorizationInfoPerms");
        return authorizationInfoPerms != null && authorizationInfoPerms.contains(authStr);
    }

    public enum UserType {
        STAFF("员工");

        private final String val;

        UserType(String val) {
            this.val = val;
        }

        public String getVal() {
            return val;
        }
    }

}
